package models

import (
	"context"
	"fmt"
	"goserver/utils"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// // bson.M (无序map)
// bson.M{"name": "张三", "age": 25}

// // bson.D (有序文档)
// bson.D{{"name", "张三"}, {"age", 25}}
// // 有序查询条件
// filter := bson.D{
//     {"name", "张三"},
//     {"age", bson.D{{"$gt", 18}}},
// }

// // 有序更新操作
// update := bson.D{
//     {"$set", bson.D{{"status", "active"}}},
//     {"$inc", bson.D{{"version", 1}}},
// }

// 泛型结构体 和 存储库初始化 逻辑，用于封装 MongoDB 数据库的通用操作
type BaseRepository[T any] struct {
	collection *mongo.Collection
}

func NewBaseRepository[T any](db *mongo.Database, collectionName string) *BaseRepository[T] {
	return &BaseRepository[T]{
		collection: db.Collection(collectionName),
	}
}

// - reflect.ValueOf(document)

// - 获取输入参数的反射值对象
// - 可以处理任何类型的值（包括指针）
// - Kind() == reflect.Ptr

// - 检查值是否为指针类型
// - 避免对非指针值调用Elem()导致panic
// - v.Elem()

// - 获取指针指向的实际值
// - 如果是nil指针会panic
// - 典型应用场景 ：

// - 处理可能传入指针或值的函数参数
// - 通用数据解析/序列化时
// - ORM映射字段时
// - 示例转换过程 ：
// type User struct{Name string}
// u := &User{Name: "张三"}

// // 反射处理前: *main.User
// v := reflect.ValueOf(u)
// // 反射处理后: main.User (通过Elem()获取)
//
//	if v.Kind() == reflect.Ptr {
//	    v = v.Elem()
//	}
//

// 通用数据库新增
func (r *BaseRepository[T]) Insert(document T, keyProps string) error {

	//如果用户传入了主键 则检测 document[keyProps] 是否存在，如果存在则在数据库中查询，如果查询到说明有重复值则返回错误，否则插入数据库
	// fmt.Println("document", document)
	if keyProps != "" {
		var result T
		data, ok := utils.GetFieldValue(document, keyProps) // 假设 keyProps 是 "Name" 或 "ID" 或其他字段名
		if !ok {
			return fmt.Errorf("属性 %s 不存在", keyProps)
		}
		query := bson.M{keyProps: data}
		err := r.collection.FindOne(context.TODO(), query).Decode(&result)
		if err == mongo.ErrNoDocuments {
			// 如果没有传入主键 则直接插入数据库
			_, err := r.collection.InsertOne(context.TODO(), document)
			if err != nil {
				return fmt.Errorf("数据库插入%w", err)
			}
			return nil
			// return fmt.Errorf("数据库插入%w", err)
		} else {
			return fmt.Errorf("属性 %s 已存在", keyProps)
		}
	} else {
		// 如果没有传入主键 则直接插入数据库
		_, err := r.collection.InsertOne(context.TODO(), document)
		return err
	}
}

// 根据ID查询
func (r *BaseRepository[T]) FindByID(id primitive.ObjectID) (*T, error) {
	var result T
	err := r.collection.FindOne(context.TODO(), bson.M{"_id": id}).Decode(&result)
	if err == mongo.ErrNoDocuments {
		return nil, nil
	}
	return &result, err
}

// 分页查找列表 并返回总数 page 页码，pageSize 每页数量 查询结果
func (r *BaseRepository[T]) FindListWithPagination(filter bson.M, page int64, pageSize int64) ([]T, int64, error) {
	var results []T
	var total int64 = 0
	// 计算跳过的记录数
	skip := (page - 1) * pageSize

	// 执行查询
	cursor, err := r.collection.Find(context.TODO(), filter, &options.FindOptions{
		Skip:  &skip,
		Limit: &pageSize,
	})
	if err != nil {
		return nil, 0, fmt.Errorf("查询失败: %w", err)
	}

	defer cursor.Close(context.TODO())
	// 使用 Find() 获取一个游标（指向查询结果）使用 cursor.All(ctx, &results) 将游标中的所有文档解码为 []User 类型，并赋值给 results  数据库类型转 本地使用的类型
	if cursorErr := cursor.All(context.TODO(), &results); cursorErr != nil {
		return nil, 0, fmt.Errorf("解析数据失败: %w", err)
	}
	// 计算总数
	total, err = r.collection.CountDocuments(context.TODO(), filter)
	if err != nil {
		return nil, 0, fmt.Errorf("统计总数失败: %w", err)
	}

	return results, total, nil
}

// 根据某个属性查询
func (r *BaseRepository[T]) FindOne(filter bson.M) (*T, error) {
	var result T
	err := r.collection.FindOne(context.TODO(), filter).Decode(&result)
	if err == mongo.ErrNoDocuments {
		return nil, nil
	}
	return &result, err
}

// 通用更新方法，根据ID更新文档
func (r *BaseRepository[T]) Update(id primitive.ObjectID, update bson.M) error {
	_, err := r.collection.UpdateOne(context.TODO(), bson.M{"_id": id}, bson.M{"$set": update})
	return err
}

// 删除方法，根据ID删除文档
func (r *BaseRepository[T]) DeleteById(id primitive.ObjectID) error {
	_, err := r.collection.DeleteOne(context.TODO(), bson.M{"_id": id})
	return err
}
